package io.noties.markwon.plugin.typogram.bean

import java.util.regex.Pattern
import kotlin.math.max


/**
 * This is a TextGrid (usually 3x3) that contains the equivalent of a
 * 2D reqular expression (which uses custom syntax to make things more
 * visual, but standard syntax is also possible).
 *
 * The custom syntax is:
 * . means anything
 * b means any boundary (any of  - = / \ + | :)
 * ! means not boundary (none of  - = / \ + | :)
 * - means - or =
 * | means | or :
 * [ means not | nor :
 * ~ means not - nor =
 * ^ means a boundary but not - nor =
 * ( means a boundary but not | nor :
 * s means a straight boundary (one of - = + | :)
 * S means not a straight boundary (none of - = + | :)
 *
 * 1 means a cell that has entry point 1
 * 2 means a cell that has entry point 2
 * 3 means a cell that has entry point 3 etc. up to number 8
 *
 * %1 means a cell that does not have entry point 1 etc.
 *
 * See below for an explanation of entry points
 *
 * +, \, / and the space are literal (as is any other character)
 *
 *
 * Entry points
 *
 * <pre>
 * 1   2   3
 * *--*--*
 * |     |
 * 8*     *4
 * |     |
 * *--*--*
 * 7   6   5
</pre> *
 *
 * We number the entry points for each cell as in the diagram
 * above. If a cell is occupied by a character, we define as
 * entry points the points of the above diagram that the character
 * can touch with the end of its lines. For example - has
 * entry points 8 and 4, | and : have entry points 2 and 6,
 * / has 3 and 7, \ has 1 and 5, + has 2, 6, 8 and 4 etc.
 *
 *
 * @author Efstathios Sideris
 */
class GridPattern : TextGrid {
    private val regExps: ArrayList<Pattern> = ArrayList<Pattern>() //TODO optimise: store as PatternS
    private var regExpsAreValid = false
    private var usesStandardSyntax = false

    constructor() : super(3, 3) {}

    constructor(row1: String, row2: String, row3: String) : super(max(max(row1.length, row2.length), row3.length), 3) {
        setTo(row1, row2, row3)
        regExpsAreValid = false
    }

    fun usesStandardSyntax(): Boolean {
        return usesStandardSyntax
    }

    fun setUsesStandardSyntax(b: Boolean) {
        usesStandardSyntax = b
        regExpsAreValid = false
    }

    fun isMatchedBy(grid: TextGrid): Boolean {
        /*if(grid.getHeight() != this.getHeight()
			|| grid.getWidth() != this.getWidth()) return false;*/
        if (!regExpsAreValid) prepareRegExps()
        for (i in 0 until grid.height) {
            val row = grid.getRow(i).toString()
            val regexp: Pattern = regExps[i]
            if (!regexp.matcher(row).matches()) {
                if (DEBUG) println("$row does not match $regexp")
                return false
            }
        }
        return true
    }

    private fun prepareRegExps() {
        regExpsAreValid = true
        regExps.clear()
        if (DEBUG) println("Trying to match:")
        if (!usesStandardSyntax) {
            val it: Iterator<StringBuffer> = rows.iterator()
            while (it.hasNext()) {
                val row = it.next().toString()
                regExps.add(Pattern.compile(makeRegExp(row)))
                if (DEBUG) println(row + " becomes " + makeRegExp(row))
            }
        } else {
            val it: Iterator<StringBuffer> = rows.iterator()
            while (it.hasNext()) {
                val row = it.next().toString()
                regExps.add(Pattern.compile(row))
            }
        }
    }

    private fun makeRegExp(pattern: String): String {
        val result = StringBuilder()
        var tokensHandled = 0
        var i = 0
        while (i < pattern.length && tokensHandled < 3) {
            var c = pattern[i]
            if (c == '[') {
                result.append("[^|:]")
            } else if (c == '|') {
                result.append("[|:]")
            } else if (c == '-') {
                result.append("-")
            } else if (c == '!') {
                result.append("[^-=\\/\\\\+|:]")
            } else if (c == 'b') {
                result.append("[-=\\/\\\\+|:]")
            } else if (c == '^') {
                result.append("[\\/\\\\+|:]")
            } else if (c == '(') {
                result.append("[-=\\/\\\\+]")
            } else if (c == '~') {
                result.append(".")
            } else if (c == '+') {
                result.append("\\+")
            } else if (c == '\\') {
                result.append("\\\\")
            } else if (c == 's') {
                result.append("[-=+|:]")
            } else if (c == 'S') {
                result.append("[\\/\\\\]")
            } else if (c == '*') {
                result.append("\\*")

                //entry points
            } else if (c == '1') {
                result.append("[\\\\]")
            } else if (c == '2') {
                result.append("[|:+\\/\\\\]")
            } else if (c == '3') {
                result.append("[\\/]")
            } else if (c == '4') {
                result.append("[-=+\\/\\\\]")
            } else if (c == '5') {
                result.append("[\\\\]")
            } else if (c == '6') {
                result.append("[|:+\\/\\\\]")
            } else if (c == '7') {
                result.append("[\\/]")
            } else if (c == '8') {
                result.append("[-=+\\/\\\\]")

                //entry point negations
            } else if (c == '%') {
                if (i + 1 > pattern.length) {
                    throw RuntimeException("Invalid pattern, found % at the end")
                }
                c = pattern[++i]
                if (c == '1') {
                    result.append("[^\\\\]")
                } else if (c == '2') {
                    result.append("[^|:+\\/\\\\]")
                } else if (c == '3') {
                    result.append("[^\\/]")
                } else if (c == '4') {
                    result.append("[^-=+\\/\\\\]")
                } else if (c == '5') {
                    result.append("[^\\\\]")
                } else if (c == '6') {
                    result.append("[^|:+\\/\\\\]")
                } else if (c == '7') {
                    result.append("[^\\/]")
                } else if (c == '8') {
                    result.append("[^-=+\\/\\\\]")
                }
            } else result.append(c.toString())
            tokensHandled++
            i++
        }
        return result.toString()
    }

    fun setTo(row1: String, row2: String, row3: String) {
        if (height != 3) throw RuntimeException("This method can only be called for GridPatternS with height 3")
        regExpsAreValid = false
        writeStringTo(0, 0, row1)
        writeStringTo(0, 1, row2)
        writeStringTo(0, 2, row3)
        //don't use setRow() here!
    }

    companion object {
        private const val DEBUG = false
        @JvmStatic
        fun main(args: Array<String>) {
            val grid = TextGrid(3, 3)
            //		grid.setRow(0, "   ");
//		grid.setRow(1, "-\\ ");
//		grid.setRow(2, " | ");
//
//		if(GridPatternGroup.corner2Criteria.isAnyMatchedBy(grid)){
//			System.out.println("Grid is corner 2");
//		} else {
//			System.out.println("Grid is not corner 2");
//		}
//
//		if(grid.isCorner2(grid.new Cell(1,1))){
//			System.out.println("Grid is corner 2");
//		} else {
//			System.out.println("Grid is not corner 2");
//		}
//
//
//		grid.setRow(0, "-+ ");
//		grid.setRow(1, " | ");
//		grid.setRow(2, "-+ ");
//
//		if(GridPatternGroup.cornerCriteria.isAnyMatchedBy(grid)){
//			System.out.println("Grid is corner");
//		} else {
//			System.out.println("Grid is not corner");
//		}
//
//		if(grid.isCorner(grid.new Cell(1,1))){
//			System.out.println("Grid is corner");
//		} else {
//			System.out.println("Grid is not corner");
//		}
            grid.setRow(0, "---")
            grid.setRow(1, " / ")
            grid.setRow(2, "---")
            grid.printDebug()
            if (GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)) {
                println("Grid is lone diagonal")
            } else {
                println("Grid is not lone diagonal")
            }
            grid.setRow(0, "--/")
            grid.setRow(1, " / ")
            grid.setRow(2, "---")
            grid.printDebug()
            if (GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)) {
                println("Grid is lone diagonal")
            } else {
                println("Grid is not lone diagonal")
            }
            grid.setRow(0, "-- ")
            grid.setRow(1, " \\ ")
            grid.setRow(2, "---")
            grid.printDebug()
            if (GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)) {
                println("Grid is lone diagonal")
            } else {
                println("Grid is not lone diagonal")
            }
            grid.setRow(0, "-- ")
            grid.setRow(1, " \\ ")
            grid.setRow(2, "--\\")
            grid.printDebug()
            if (GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)) {
                println("Grid is lone diagonal")
            } else {
                println("Grid is not lone diagonal")
            }
            grid.setRow(0, "   ")
            grid.setRow(1, "-\\/")
            grid.setRow(2, " ||")
            grid.printDebug()
            if (GridPatternGroup.loneDiagonalCriteria.isAnyMatchedBy(grid)) {
                println("Grid is lone diagonal")
            } else {
                println("Grid is not lone diagonal")
            }
        }
    }
}
